package cn.duyo.spring.aop.framework;

import cn.duyo.spring.aop.AdvisedSupport;
import cn.duyo.spring.util.ClassUtils;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodProxy;
import net.sf.cglib.proxy.MethodInterceptor;

import java.lang.reflect.Method;

/**
 * 基于 Cglib 实现的代理类 (基于ASM字节码生成子类, 所以不能是 final 类型)
 * TODO 由于 Cglib 本身的设计不支持多重代理，无法实现在 Proxy 外面再包装一层 Proxy，即无法在Cglib生成的对象再使用Cglib生成代理对象（JDK Proxy 可以）
 *   spring中一个目标方法被多个 AOP 拦截，此时就需要多重代理
 *
 * https://segmentfault.com/a/1190000022839851 （CGLIB动态代理机制）
 * https://blog.csdn.net/weixin_33937778/article/details/94653996（使用 Cglib 实现多重代理）
 * @author du
 */
public class Cglib2AopProxy implements AopProxy {

    private final AdvisedSupport advised;

    public Cglib2AopProxy(AdvisedSupport advised) {
        this.advised = advised;
    }

    @Override
    public Object getProxy() {
        Enhancer enhancer = new Enhancer();
        Class<?> aClass = advised.getTargetSource().getTarget().getClass();
        aClass = ClassUtils.isCglibProxyClass(aClass)? aClass.getSuperclass():aClass;

        enhancer.setSuperclass(aClass);
        enhancer.setInterfaces(advised.getTargetSource().getTargetClass());
        enhancer.setCallback(new DynamicAdvisedInterceptor(advised));
        return enhancer.create();
    }

    private static class DynamicAdvisedInterceptor implements MethodInterceptor {

        private final AdvisedSupport advised;

        public DynamicAdvisedInterceptor(AdvisedSupport advised) {
            this.advised = advised;
        }

        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            CglibMethodInvocation methodInvocation = new CglibMethodInvocation(advised.getTargetSource().getTarget(), method, objects, methodProxy);
            if (advised.getMethodMatcher().matches(method, advised.getTargetSource().getTarget().getClass())) {
                // 命中切点：执行AOP Alliance的增强逻辑
                return advised.getMethodInterceptor().invoke(methodInvocation);
            }
            // 未命中切点：执行CGLIB原生调用
            return methodInvocation.proceed();

            // 此方式更简洁，但由于Cglib无法多重代理无法使用
//            if (advised.getMethodMatcher().matches(method, advised.getTargetSource().getTarget().getClass())) {
//                // 命中切点：执行AOP Alliance的增强逻辑
//                return advised.getMethodInterceptor().invoke(new ReflectiveMethodInvocation(advised.getTargetSource().getTarget(), method, objects));
//            }
//            // 未命中切点：执行CGLIB原生调用
//            return methodProxy.invoke(advised.getTargetSource().getTarget(), objects);
        }
    }

    private static class CglibMethodInvocation extends ReflectiveMethodInvocation {

        private final MethodProxy methodProxy;

        public CglibMethodInvocation(Object target, Method method, Object[] arguments, MethodProxy methodProxy) {
            super(target, method, arguments);
            this.methodProxy = methodProxy;
        }

        @Override
        public Object proceed() throws Throwable {
            return this.methodProxy.invoke(this.target, this.arguments);
        }

    }
}